local famBags = LibStub("AceAddon-3.0"):GetAddon("famBags")
local lbf = LibStub("LibButtonFacade", true)
famBags.Buttons = {
   Current = {}
}
famBags.Buttons.LastBuild = ''
-- :::::::::::: LOCALS ::::::::::::: -------------------------------------------------------------------------

local function create_item_data(button)
   button.info = {
      texture = nil,
      bag = nil,
      slot = nil,
      count = nil,
      quality = nil,
      locked = nil,
      readable = nil,
   }
end

local function partial_sort_bag(b1, b2)
   local a, b = b1.info, b2.info
   if a.id and not b.id then
      return true
   elseif b.id and not a.id then
      return false
   elseif a.type and b.type and a.type ~= b.type then
      return a.type < b.type
   elseif a.subType and b.subType and a.subType ~= b.subType then
      return a.subType < b.subType
   elseif a.id and b.id then
      return a.id > b.id
   elseif a.bag ~= b.bag then
      return a.bag < b.bag
   else
      return a.slot < b.slot
   end
end

local function full_sort_bag(b1, b2)
   --if type(b1) ~= 'table' or type(b2) ~= 'table' then return nil end
   local a, b = b1.info, b2.info
   local s1, s2 = #b1.stacks, #b2.stacks
   if a.id and not b.id then
      return true
   elseif b.id and not a.id then
      return false
   elseif a.quality ~= b.quality then
      return (a.quality or -1) > (b.quality or -1)
   elseif a.isQuestItem ~= b.isQuestItem then
      return a.isQuestItem
   elseif a.type and b.type and a.type ~= b.type then
      return a.type < b.type
   elseif a.subType and b.subType and a.subType ~= b.subType then
      return a.subType < b.subType
   elseif a.id and b.id and a.id ~= b.id then
      return a.id > b.id
   elseif a.id and b.id and a.id == b.id then
      return (s1 + s2 > 0 and s1 > s2) or (s1 + s2 == s2 and a.count > b.count)
   elseif a.bag ~= b.bag then
      return a.bag < b.bag
   else
      return a.slot < b.slot
   end
end

local function custom_sort_bag(b1, b2)
   return famBags.Sorting:Sort(b1, b2)
end

local DUMMY_TOOLTIP = CreateFrame('GameTooltip')
local DUMMY_TOOLTIP_LEFT_LINES = {}
local DUMMY_TOOLTIP_RIGHT_LINES = {}
for i=1, 30 do
   DUMMY_TOOLTIP_LEFT_LINES[i],DUMMY_TOOLTIP_RIGHT_LINES[i] = DUMMY_TOOLTIP:CreateFontString(), DUMMY_TOOLTIP:CreateFontString()
   DUMMY_TOOLTIP_LEFT_LINES[i]:SetFontObject(GameFontNormal)
   DUMMY_TOOLTIP_RIGHT_LINES[i]:SetFontObject(GameFontNormal)
   DUMMY_TOOLTIP:AddFontStrings(DUMMY_TOOLTIP_LEFT_LINES[i],DUMMY_TOOLTIP_RIGHT_LINES[i])
end
DUMMY_TOOLTIP:SetOwner(UIParent, 'ANCHOR_NONE')
DUMMY_TOOLTIP:Hide()

local function get_item_bound(bag, slot)
   local bound = nil
   DUMMY_TOOLTIP:SetOwner(UIParent, 'ANCHOR_NONE')
   if bag == KEYRING_CONTAINER then
      DUMMY_TOOLTIP:SetInventoryItem('player', KeyRingButtonIDToInvSlotID(slot))
   elseif bag == BANK_CONTAINER then
      DUMMY_TOOLTIP:SetInventoryItem('player', BankButtonIDToInvSlotID(slot))
   else
      DUMMY_TOOLTIP:SetBagItem(bag,slot)
   end
   for i = 2, 4 do
      if DUMMY_TOOLTIP:NumLines() >= i then
         local s = DUMMY_TOOLTIP_LEFT_LINES[i]:GetText()
         if tContains(famBags.Criterias[FAMBAGSCRITERIA_BOUNDSTATUS].options, s) then
            bound = s
            break
         elseif DUMMY_TOOLTIP_RIGHT_LINES[i] then
            s = DUMMY_TOOLTIP_RIGHT_LINES[i]:GetText()
            if tContains(famBags.Criterias[FAMBAGSCRITERIA_BOUNDSTATUS].options, s) then
               bound = s
               break
            end
         end
      end
   end
   return bound
end
local NULL_ITEM_ID = -100
local TEMP_STATS = {}

local dbButtons = {}

-- :::::::::::: famBags.Buttons ::::::::::::: -------------------------------------------------------------------------
function famBags.Buttons:CreateBag(bag)
   local parent = tContains(famBags.BagIndexes.bags, bag) and famBags.famBagsFrame or famBags.famBankFrame
   local name = string.format('famBagsBag_%s', tostring(bag))
   self[bag] = self[bag] or _G[name] or CreateFrame("Frame", name, parent)
   self[bag]:SetID(bag)
   return self[bag]
end

function famBags.Buttons:Create(bag, slot, forceIndicators, skipUpdate)
   local bagset = (tContains(famBags.BagIndexes.bags, bag)) and 'bags' or 'bank'
   if famBags.OFFLINE_ENABLED and not dbButtons[bagset] then
      dbButtons[bagset] = famBags.db.global.offline.buttons[famBags.CURRENT_PLAYER[bagset]]
   end
   if not self[bag] then
      self:CreateBag(bag)
   end
   local b = self[bag][slot]

   local name = string.format("famBagsButton_%s_%s", tostring(bag), tostring(slot))
   local size = famBags.db.profile.layout.buttons.size
   local new_button = (not(b or _G[name]))
   b = b or _G[name] or CreateFrame("CheckButton", name, self[bag], "ContainerFrameItemButtonTemplate")
   if famBags.OFFLINE_ENABLED then
      if famBags.MAINFRAMES[bagset].offline then
         b:RegisterForClicks()
         b:RegisterForDrag()
      else
         b:RegisterForClicks("LeftButtonUp", "RightButtonUp")
         b:RegisterForDrag("LeftButton")
      end
   end
   b:SetScale(size)
   b:SetFrameLevel(7)

   self[bag][slot] = b
   local cd = _G[name.."Cooldown"]
   cd:SetAllPoints(b)
   cd:SetFrameLevel(10)

   b.quality = b.quality or CreateFrame("Frame", name..'_quality', b)
   b.quality.texture = b.quality.texture or b.quality:CreateTexture(nil, 'OVERLAY')
   famBags.Buttons:UpdateQualityGlow(b)
   b.quality:SetAllPoints()

   if not b.stackIndicator or forceIndicators then
      self:CreateIndicators(b, bag, name)
   else
      b.stackIndicator:Hide()
   end

   b.stacks = b.stacks or {}
   if famBags.OFFLINE_ENABLED then
      dbButtons[bagset][bag][slot] = dbButtons[bagset][bag][slot] or {}
      b.info = dbButtons[bagset][bag][slot]
   else
      b.info = b.info or {}
   end

   if new_button and lbf and famBags.BFBags then
      famBags.BFBags:AddButton(b,{
         Icon = _G[name.."IconTexture"]
      })
      if b.count and type(b.count) == 'table' then
         b.countFontString = b.count
         b.count = 1
      end
   end

   b:SetID(slot)
--   frame:SetScript("PreClick",famBags.Buttons:PreClick)
   if not skipUpdate then
      self:UpdateItem(b, bag, slot)
   end

   self:Update(b)
   self:UpdateButtonCooldown(b)

   return b
end

function famBags.Buttons:ClearDBButtons(bag)
   dbButtons[bag] = nil
end

function famBags.Buttons:CreateEmpty(bag, window, type)
   self.empties = self.empties or {}
   self.empties[window] = self.empties[window] or {}
   local b = self.empties[window][bag]
   local name = string.format("famBagsEmptyButton_%s_%s", tostring(window), tostring(bag))
   if not b then
      local size = famBags.db.profile.layout.buttons.size
      local dummyparentbag = _G[name..'_DummyBag'] or CreateFrame("Frame", name..'_DummyBag', famBags.Sections[window].empties)
      dummyparentbag:SetID(NULL_ITEM_ID)
      b = _G[name] or CreateFrame("CheckButton", name, dummyparentbag, "ContainerFrameItemButtonTemplate")
      b:SetFrameLevel(7)
      dummyparentbag:SetWidth(b:GetWidth())
      dummyparentbag:SetHeight(b:GetHeight())
      b:SetAllPoints(dummyparentbag)
      dummyparentbag:SetScale(size)
      b:SetID(NULL_ITEM_ID)

      self.empties[window][bag] = b

      if lbf and famBags.BFBags then
         famBags.BFBags:AddButton(b, {
            Icon = _G[name.."IconTexture"]
         })
      end

      b:Show()
      b:GetParent():Show()

      b.info = b.info or {}
      b.info.count = 0
      b.info.needUpdate = nil

   end

   if famBags.OFFLINE_ENABLED then
      if famBags.MAINFRAMES[window].offline then
         b:RegisterForClicks()
         b:RegisterForDrag()
      else
         b:RegisterForClicks("LeftButtonUp", "RightButtonUp")
         b:RegisterForDrag("LeftButton")
      end
   end

   if famBags.db.profile.layout.buttons.indicators_enabled and type > 0 then
      b.typeIndicator = _G[name..'_TypeIndicator'] or CreateFrame("Frame", name..'_TypeIndicator', b)
      b.typeIndicator.texture = b.typeIndicator.texture or b.typeIndicator:CreateTexture(nil, 'OVERLAY')
      b.typeIndicator.texture:SetTexture(string.format('Interface\\Addons\\famBags\\Textures\\%s.blp', famBags.BagTypes[type]))
      b.typeIndicator.texture:SetBlendMode('ALPHAKEY')
      b.typeIndicator.texture:SetAllPoints()
      self:UpdateIndicator(b, b.typeIndicator, 1)
      b.typeIndicator:Show()
   elseif b.typeIndicator then
      b.typeIndicator:Hide()
   end

   return b
end

function famBags.Buttons:UpdateEmpties(bag)
   local db = famBags.db.profile.layout
   for _,b in pairs(self.empties[bag]) do
      b.info.count = 0
   end
   local n, bagType
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      if not famBags.MAINFRAMES[bag].offline then
         n, bagType = GetContainerNumFreeSlots(i)
      else
         n = 0
         bagType = 0
         local offline_bags = famBags.db.global.offline.buttons[famBags.CURRENT_PLAYER[bag]]
         if offline_bags and offline_bags[i] then
            for _,info in ipairs(offline_bags[i]) do
               if not info.id then n = n + 1 end
            end
         end
      end
      if i == KEYRING_CONTAINER then
         bagType = 256
      end
      local index = db.general.sections ~= 'bags' and bagType or i
      local b = self.empties[bag][index]
      if b then -- TODO: ???? should't happen nil error here
         b.info.count = b.info.count + n
      end
   end
   for n, b in pairs(self.empties[bag]) do
      local c = _G[b:GetName().."Count"]
      if n == 256 or n == KEYRING_CONTAINER then
         c:SetText(string.format('|cff%02x%02x%02x%s|r', db.counts.color[1]*255, db.counts.color[2]*255, db.counts.color[3]*255, '*'))
      elseif b.info.count > 0 then
         c:SetText(string.format('|cff%02x%02x%02x%s|r', db.counts.color[1]*255, db.counts.color[2]*255, db.counts.color[3]*255, tostring(b.info.count)))
      else
         c:SetText(string.format('|cff%02x%02x%02x%s|r', db.empties.no_empty_color[1]*255, db.empties.no_empty_color[2]*255, db.empties.no_empty_color[3]*255, db.empties.no_empty_text))
         b:SetID(NULL_ITEM_ID)
         b:GetParent():SetID(NULL_ITEM_ID)
      end
      c:Show()
   end
end

function famBags.Buttons:CreateIndicators(b, bag, name)
   name = name or b:GetName()
   if famBags.db.profile.layout.buttons.indicators_enabled then
      if self[bag].bagType and self[bag].bagType > 0 then
         if not b.typeIndicator then
            b.typeIndicator = _G[name..'_TypeIndicator'] or CreateFrame("Frame", name..'_TypeIndicator', b)
            b.typeIndicator.texture = b.typeIndicator.texture or b.typeIndicator:CreateTexture(nil, 'OVERLAY')
         end
         if (famBags.BagTypes[self[bag].bagType]) then
            b.typeIndicator.texture:SetTexture(string.format('Interface\\Addons\\famBags\\Textures\\%s.blp', famBags.BagTypes[self[bag].bagType]))
         end
         b.typeIndicator.texture:SetBlendMode('ALPHAKEY')
         b.typeIndicator.texture:SetAllPoints()
         self:UpdateIndicator(b, b.typeIndicator, 1)
         b.typeIndicator:Show()
      elseif b.typeIndicator then
         b.typeIndicator:Hide()
      end

      if not b.stackIndicator then
         if not b.stackIndicator then
            b.stackIndicator = _G[name..'_StackIndicator'] or CreateFrame("Frame", name..'_StackIndicator', b)
            b.stackIndicator.texture = b.stackIndicator.texture or b.stackIndicator:CreateTexture(nil, 'OVERLAY')
         end
         b.stackIndicator.texture:SetTexture('Interface\\Addons\\famBags\\Textures\\stack.blp')
         b.stackIndicator.texture:SetBlendMode('ALPHAKEY')
         b.stackIndicator.texture:SetAllPoints()
      end
      self:UpdateIndicator(b, b.stackIndicator, 2)
      b.stackIndicator:Hide()
   else
      if b.typeIndicator then b.typeIndicator:Hide() end
      if b.stackIndicator then b.stackIndicator:Hide() end
   end
end

function famBags.Buttons:UpdateAllIndicators()
   for _, bag in pairs(famBags.BagIndexes) do
      self:UpdateWindowIndicators(bag)
   end
end

function famBags.Buttons:UpdateWindowIndicators(window)
   for _, i in ipairs(window) do
      self:UpdateBagIndicators(i)
   end
end

function famBags.Buttons:UpdateBagIndicators(bag)
   if self[bag] then
      for _, b in ipairs(self[bag]) do
         self:CreateIndicators(b, bag)
         self:UpdateStatusIndicator(b)
      end
   end
end

function famBags.Buttons:UpdateIndicator(b, indicator, corner) -- corner = topleft:1; topright:2; bottomleft:3;
   local db = famBags.db.profile.layout.buttons
   if corner == 1 then
      indicator:SetPoint('TOPLEFT', b, 'TOPLEFT', db.indicators_offset, -db.indicators_offset)
   elseif corner == 2 then
      indicator:SetPoint('TOPRIGHT', b, 'TOPRIGHT', -db.indicators_offset, -db.indicators_offset)
   else
      indicator:SetPoint('BOTTOMLEFT', b, 'BOTTOMLEFT', db.indicators_offset, db.indicators_offset)
   end
   indicator:SetWidth(16 * db.indicators_size)
   indicator:SetHeight(16 * db.indicators_size)
   indicator:SetFrameLevel(12)
end

function famBags.Buttons:ResetUpdatedIndicators()
   for _, bag in ipairs(famBags.BagIndexes.bags) do
      if self[bag] then
         for _, b in ipairs(self[bag]) do
            if b.info.new or b.info.added then
               b.info.new = nil
               b.info.added = nil
               self:UpdateStatusIndicator(b)
            end
         end
      end
   end
end

function famBags.Buttons:UpdateStatusIndicator(b)
   if (not famBags.db.profile.layout.buttons.indicators_enabled) or (not(b.info.new or b.info.added)) then
      if b.statusIndicator then
         b.statusIndicator:Hide()
      end
      return
   end
   if not b.statusIndicator then
      local name = b:GetName()..'_StatusIndicator'
      b.statusIndicator = _G[name] or CreateFrame("Frame", name, b)
      b.statusIndicator.texture = b.statusIndicator.texture or b.statusIndicator:CreateTexture(nil, 'OVERLAY')
   end
   b.statusIndicator.texture:SetTexture(b.info.new and 'Interface\\Addons\\famBags\\Textures\\new.blp' or 'Interface\\Addons\\famBags\\Textures\\added.blp')
   b.statusIndicator.texture:SetBlendMode('ALPHAKEY')
   b.statusIndicator.texture:SetAllPoints()
   self:UpdateIndicator(b, b.statusIndicator, 3)
   b.statusIndicator:Show()
end

function famBags.Buttons:GetButton(bag, slot)
   if not (self[bag] and #self[bag]) then return nil end
   if self[bag][slot] and self[bag][slot].info and self[bag][slot].info.slot == slot then return self[bag][slot] end
   for _, b in ipairs(self[bag]) do
      if b.info.slot == slot then return b end
   end
   return nil
end

function famBags.Buttons:UpdateButtonsCooldown()
   for _, indexes in pairs(famBags.BagIndexes) do
      for _, i in ipairs(indexes) do
         if self[i] then
            for _, b in ipairs(self[i]) do
               self:UpdateButtonCooldown(b)
            end
         end
      end
   end
end

function famBags.Buttons:UpdateButtonCooldown(button)
   if not (button and button.info.bag and button.info.slot) then return end
   local cd = _G[button:GetName().."Cooldown"];
   local start, duration, enable = GetContainerItemCooldown(button.info.bag, button.info.slot);
   CooldownFrame_SetTimer(cd, start, duration, enable);
   if ( duration > 0 and enable == 0 ) then
      SetItemButtonTextureVertexColor(button, 0.4, 0.4, 0.4);
   end
   if enable then
      cd:Show()
   else
      cd:Hide()
   end
end

function famBags.Buttons:UpdateButtonsScale()
   local size = famBags.db.profile.layout.buttons.size
   for _, indexes in pairs(famBags.BagIndexes) do
      for _, i in ipairs(indexes) do
         if self[i] then
            for _, b in ipairs(self[i]) do
               b:SetScale(size)
            end
         end
      end
   end
end

function famBags.Buttons:OrderBag(container, ordered)
   table.sort(container, (ordered) and ((famBags.db.profile.layout.order.default) and full_sort_bag or custom_sort_bag) or partial_sort_bag)
end

function famBags.Buttons:SearchStack(section, button)
   local db = famBags.db.profile.layout.stacks
   local id = button.info.id
   if (db.only_stackable and id and button.info.stack == 1) or
      (db.only_full and id and button.info.stack ~= button.info.count) then
      return nil
   end
   for _, b in ipairs(section) do
      if b.info.id == id and
         ((not id) or
          ((not db.only_full) or (b.info.stack == b.info.count))) then
         return b
      end
   end
   return nil
end

function famBags.Buttons:BuildCurrentFromBags(bag, ordered, onebag)
   self:WipeCurrent(bag, onebag or (self.LastBuild ~= 'bags'))
   local offline = famBags.MAINFRAMES[bag].offline
   local container
   if onebag then
      self.Current[bag] = self.Current[bag] or {}
      container = self.Current[bag]
   else
      famBags.Sections:BuildBagsSections(bag)
   end
   for _,b in pairs(self.empties[bag]) do
      b.info.assigned = nil
   end
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      local slots
      if not offline then
         slots = GetContainerNumSlots(i)
      end
      if not self[i] or offline or (slots and slots ~= #self[i]) then
         famBags:UpdateBagButtons(i)
         if self[i] then
            slots = #self[i]
         end
      end
      if slots and slots > 0 then
         if not onebag then
            self.Current[bag][i] = self.Current[bag][i] or {}
            container = self.Current[bag][i]
         end
         for j=1, slots do
            local b = self[i][j]
            if not(famBags[bag..'SearchFilter']) or not(b.info.name) or strfind(strupper(b.info.name), famBags[bag..'SearchFilter']) then
               if famBags.isStackMode then
                  if b.info.id then
                     local b1 = self:SearchStack(container, b)
                     if b1 then
                        table.insert(b1.stacks, b)
                        b.stacker = b1
                        b.stackerIndex = #b1.stacks
                        b.container = b.stacker.container
                        b.containerIndex = b.stacker.containerIndex
                     else
                        table.insert(container, b)
                        b.stacker = nil
                        b.stackerIndex = nil
                        b.container = container
                        b.containerIndex = #container
                     end
                  else
                     local index = onebag and self[i].bagType or i
                     if not self.empties[bag][index].info.assigned then
                        local b2 = self.empties[bag][index]
                        b2:SetID(j)
                        b2:GetParent():SetID(i)
                        b2.info.assigned = true
                     end
                  end
               else
                  table.insert(container, b)
                  b.stacker = nil
                  b.stackerIndex = nil
                  b.container = container
                  b.containerIndex = #container
               end
            end
         end
         if not onebag then
            if (ordered or famBags.isStackMode) then
               self:OrderBag(container, ordered)
            end
            self:UpdateCounts(container)
         end
      end
   end
   if onebag then
      if (ordered or famBags.isStackMode) then
         self:OrderBag(container, ordered)
      end
      self:UpdateCounts(container)
   end
   self:UpdateEmpties(bag)
   self.LastBuild = onebag and 'onebag' or 'bags'
end

function famBags.Buttons:UpdateCurrentFromBags(bag, onebag)
   local container
   if onebag then
      container = self.Current[bag]
   end
   for _,b in pairs(self.empties[bag]) do
      b.info.assigned = nil
   end
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      local slots = GetContainerNumSlots(i)
      if slots and slots > 0 then
         if not onebag then
            container = self.Current[bag][i]
         end
         for j=1, slots do
            local b = self[i][j]
            if famBags.isStackMode and b.info.id and ((not b.container) or (b.stacker and b.stacker.container ~= b.container)) then
               local b1 = self:SearchStack(container, b)
               if b1 and not tContains(b1.stacks, b) then
                  table.insert(b1.stacks, b)
                  b.stacker = b1
                  b.stackerIndex = #b1.stacks
                  b.container = container
                  b.containerIndex = #container
               elseif not b1 then
                  if b.containerIndex then
                     container[b.containerIndex] = b
                  end
                  b.stacker = nil
                  b.stackerIndex = nil
                  b.container = container
                  b.containerIndex = #container
                  wipe(b.stacks)
               end
            elseif famBags.isStackMode and not b.info.id and not self.empties[bag][onebag and 0 or i].info.assigned then
               local index = onebag and 0 or i
               local b2 = self.empties[bag][index]
               b2:SetID(j)
               b2:GetParent():SetID(index)
               b2.info.assigned = true
            end
         end
         if not onebag then
            self:UpdateCounts(container)
         end
      end
   end
   if onebag then
      self:UpdateCounts(container)
   end
   self:UpdateEmpties(bag)
   self.LastBuild = onebag and 'onebag' or 'bags'
end

function famBags.Buttons:GetButtonCategoryBag(bag, info)
   if famBags[bag..'SearchFilter'] and info.name and not strfind(strupper(info.name), famBags[bag..'SearchFilter']) then
      return nil
   end
   local cat = famBags.Classification:GetButtonCategory(info, famBags.SortedCategories[bag])
   if cat then
      self.Current[bag][cat] = self.Current[bag][cat] or {}
      return self.Current[bag][cat]
   end
   return nil
end

function famBags.Buttons:BuildCurrentFromCategories(bag, ordered)
   famBags.Classification:ResetWrongCategories()
   local offline = famBags.MAINFRAMES[bag].offline
   self:WipeCurrent(bag, self.LastBuild ~= 'categories')
   famBags.Sections:BuildCategoriesSections(bag)
   for _,b in pairs(self.empties[bag]) do
      b.info.assigned = nil
   end
   local b, container, b1
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      local slots
      if not offline then
         slots = GetContainerNumSlots(i)
      end
      if not self[i] or offline or (slots and slots ~= #self[i]) then
         famBags:UpdateBagButtons(i)
         if self[i] then
            slots = #self[i]
         end
      end
      if slots then
         for j=1, slots do
            b = self[i][j]
            container = self:GetButtonCategoryBag(bag, b.info)
            if container then
               if famBags.isStackMode then
                  if b.info.id then
                     b1 = self:SearchStack(container, b)
                     if b1 then
                        table.insert(b1.stacks, b)
                        b.stacker = b1
                        b.stackerIndex = #b1.stacks
                        b.container = b.stacker.container
                        b.containerIndex = b.stacker.containerIndex
                     else
                        table.insert(container, b)
                        b.stacker = nil
                        b.stackerIndex = nil
                        b.container = container
                        b.containerIndex = #container
                     end
                  elseif not self.empties[bag][self[i].bagType].info.assigned then
                     local b2 = self.empties[bag][self[i].bagType]
                     b2:SetID(j)
                     b2:GetParent():SetID(i)
                     b2.info.assigned = true
                  end
               else
                  table.insert(container, b)
                  b.stacker = nil
                  b.stackerIndex = nil
                  b.container = container
                  b.containerIndex = #container
               end
            end
         end
      end
   end
   for _, container in pairs(self.Current[bag]) do
      if (ordered or famBags.isStackMode) then
         self:OrderBag(container, ordered)
      end
      self:UpdateCounts(container)
   end
   self:UpdateEmpties(bag)
   self.LastBuild = 'categories'
end

function famBags.Buttons:UpdateCurrentFromCategories(bag)
   famBags.Classification:ResetWrongCategories()
   for _,b in pairs(self.empties[bag]) do
      b.info.assigned = nil
   end
   local b, container, b1
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      local slots = GetContainerNumSlots(i)
      if slots and self[i] then
         for j=1, slots do
            b = self[i][j]
            if famBags.isStackMode and b.info.id and ((not b.container) or (b.stacker and (b.stacker.container ~= b.container or b.stacker.info.id ~= b.info.id))) then
               container = b.container or self:GetButtonCategoryBag(bag, b.info)
               if container then
                  b1 = self:SearchStack(container, b, true)
                  if b1 and not tContains(b1.stacks, b) then
                     table.insert(b1.stacks, b)
                     b.stacker = b1
                     b.stackerIndex = #b1.stacks
                     b.container = container
                     b.containerIndex = b1.containerIndex
                  elseif not b1 then
                     if b.containerIndex then
                        container[b.containerIndex] = b
                     end
                     b.stacker = nil
                     b.stackerIndex = nil
                     b.container = container
                     b.containerIndex = b.containerIndex or #container
                     wipe(b.stacks)
                     b:Show()
                  end
               end
            elseif famBags.isStackMode and not b.info.id and not self.empties[bag][self[i].bagType].info.assigned then
               local b2 = self.empties[bag][self[i].bagType]
               b2:SetID(j)
               b2:GetParent():SetID(i)
               b2.info.assigned = true
            end
         end
      end
   end
   if self.Current[bag] then
      for _, container in pairs(self.Current[bag]) do
         self:UpdateCounts(container)
      end
   end
   self:UpdateEmpties(bag)
   self.LastBuild = 'categories'
end

function famBags.Buttons:UpdateItemLock(button, bag, slot)
   _, _, button.info.locked = GetContainerItemInfo(bag, slot)
end

function famBags.Buttons:UpdateItem(button, bag, slot)
   local frame = (tContains(famBags.BagIndexes.bags, bag)) and 'bags' or 'bank'
   if famBags.MAINFRAMES[frame].offline then return end
   local info = button.info
   info.bag = bag
   info.slot = slot

   local oldid, oldcount = info.id, info.count
   info.id = GetContainerItemID(bag, slot)

   local texture, count, locked, _, readable, lootable, link = GetContainerItemInfo(bag, slot)

   if oldid == info.id and oldcount == count then return end

   if oldid ~= info.id then
      if info.id then
         if frame == 'bags' then
            famBags.BAGS_NEED_LAYOUT = true
         elseif frame == 'bank' then
            famBags.BANK_NEED_LAYOUT = true
         end
      else
         button:Hide()
         wipe(button.stacks)
         if button.stacker then
            table.remove(button.stacker.stacks, button.stackerIndex)
            if button.stacker.stacks[button.stackerIndex] then
               for i=button.stackerIndex, #button.stacker.stacks do
                  button.stacker.stacks[i].stackerIndex = i
               end
            end
            button.stacker = nil
            button.stackerIndex = nil
         end
         button.container = nil
         button.containerIndex = nil
      end
   end

   if frame == 'bags' then
      info.new = nil
      local needaddedupdate
      if info.id then
         local newcount = famBags:GetInventoryForItem(info.id)
         local oldcount = famBags.Inventory[info.id] or 0
         if oldid ~= info.id then
            if type(newcount) == 'string' then
               if newcount == 'new' or (newcount == 'added' and oldcount == 0) then
                  info.new = not famBags.FIRST_BUILD
               else
                  info.added = true
                  needaddedupdate = true
               end
            else
               if (newcount > oldcount) then
                  if oldcount == 0 then
                     info.new = not famBags.FIRST_BUILD
                  else
                     info.added = true
                     needaddedupdate = true
                  end
               end
            end
         elseif (type(newcount) == 'number' and newcount > oldcount) or newcount == 'added' then
            info.added = true
            needaddedupdate = true
         end
         if needaddedupdate then
            for _, bag in ipairs(famBags.BagIndexes.bags) do
               if self[bag] then
                  for _, b in ipairs(self[bag]) do
                     if info.id == b.info.id then
                        b.info.added = true
                        self:UpdateStatusIndicator(b)
                     end
                  end
               end
            end
         end
      end
   end

   info.texture, info.count, info.locked, info.readable, info.lootable, info.link = texture, count, locked, readable, lootable, link
   button.count = count

   info.stats = info.stats or {}
   wipe(info.stats)
   wipe(TEMP_STATS)
   if info.id then
      local name, _, quality, iLvl, minLvl, type, subType, stack, equipLoc, _, sellPrice = GetItemInfo(info.id)
      local isQuestItem, questID, isActive = GetContainerItemQuestInfo(bag, slot)
      info.name, info.quality, info.iLvl, info.minLvl, info.type, info.subType, info.stack, info.equipLoc, info.sellPrice, info.isQuestItem, info.questID, info.isActive =
      name, quality, iLvl, minLvl, type, subType, stack, equipLoc, sellPrice, isQuestItem, questID, isActive
      info.spell = GetItemSpell(info.id)
      info.bound = get_item_bound(bag, slot)
      GetItemStats(info.link, TEMP_STATS)
      if next(TEMP_STATS) then
         for n, v in pairs(TEMP_STATS) do
            if famBags.Criterias[FAMBAGSCRITERIA_STATS].options[n] then
               table.insert(info.stats, n)
            end
         end
      end
   else
      info.name,
      info.quality,
      info.iLvl,
      info.minLvl,
      info.type,
      info.subType,
      info.stack,
      info.equipLoc,
      info.sellPrice,
      info.isQuestItem,
      info.questID,
      info.isActive,
      info.spell,
      info.bound = nil
   end
end

function famBags.Buttons:Update(button)
   if not button.info then return end
   local info = button.info

   local name = button:GetName()
   local count = _G[name.."Count"]

   SetItemButtonTexture(button, info.texture)
   self:UpdateLock(button)

   if info.count and info.count > 1 then
      famBags:SetTextLayout(button, count, famBags.db.profile.layout.counts, tostring(info.count), false, false)
      count:Show()
   else
      famBags:SetTextLayout(button, count, famBags.db.profile.layout.counts, '', false, false)
      count:Hide()
   end

   local questTexture = (info.questID and not info.isActive) and TEXTURE_ITEM_QUEST_BANG or (info.questID or info.isQuestItem) and TEXTURE_ITEM_QUEST_BORDER

   if questTexture then
      button.quality.texture:SetTexture(questTexture)
      button.quality.texture:SetBlendMode('BLEND')
      button.quality.texture:SetVertexColor(1,1,1)
      button.quality.texture:SetAlpha(1)
      button.quality.texture:Show()
   elseif button.info.quality and button.info.quality > 1 and famBags.db.profile.layout.buttons.quality_glow ~= 'none' then
      local r, g, b = GetItemQualityColor(button.info.quality)
      button.quality.texture:SetBlendMode('ADD')
      button.quality.texture:SetVertexColor(r, g, b, famBags.db.profile.layout.buttons.quality_glow_alpha)
      button.quality.texture:Show()
   else
      button.quality.texture:Hide()
   end

   self:UpdateStatusIndicator(button)
end

function famBags.Buttons:UpdateQualityGlow(b, update)
   local db = famBags.db.profile.layout.buttons
   if db.quality_glow ~= 'none' then
      b.quality.texture:SetTexture(string.format('Interface\\Addons\\famBags\\Textures\\Glow_%s.tga', db.quality_glow))
      b.quality.texture:SetBlendMode('ADD')
      b.quality.texture:SetPoint('TOPLEFT', b.quality, 'TOPLEFT', db.quality_glow_inset, -db.quality_glow_inset)
      b.quality.texture:SetPoint('BOTTOMRIGHT', b.quality, 'BOTTOMRIGHT', -db.quality_glow_inset, db.quality_glow_inset)
   else
      b.quality.texture:Hide()
   end
   if update then
      self:Update(b)
   end
end

function famBags.Buttons:UpdateAllButtonsQuality()
   for _, indexes in pairs(famBags.BagIndexes) do
      for _, i in ipairs(indexes) do
         if self[i] then
            for _, b in ipairs(self[i]) do
               self:UpdateQualityGlow(b, true)
            end
         end
      end
   end
end

function famBags.Buttons:UpdateLock(button)
   if not button.info then return end
   SetItemButtonDesaturated(button, button.info.locked)
   if button.info.locked and button.stacks and #button.stacks > 0 then
      local index
      for i, b in ipairs(button.stacks) do
         if not b.info.locked and b.info.id == button.info.id then
            index = i
            break
         end
      end
      if index then
         local swap = button.stacks[index]
         button.stacks, swap.stacks = swap.stacks, button.stacks
         swap.stacks[index] = button
         button.stacker = swap
         button.stackerIndex = index
         swap.stacker = nil
         swap.stackerIndex = nil
         if button.container then
            button.container[button.containerIndex] = swap
         end
         swap:SetPoint(button:GetPoint(1))
         local buttontext = _G[button:GetName().."Count"]
         local swaptext = _G[swap:GetName().."Count"]
         local t = buttontext:GetText()
         buttontext:SetText(swaptext:GetText())
         swaptext:SetText(t)
         if button.stackIndicator and button.stackIndicator:IsVisible() then
            button.stackIndicator:Hide()
            swap.stackIndicator:Show()
         end
         button:Hide()
         swap:Show()
      end
   end
end

function famBags.Buttons:UpdateCounts(section)
   for index, b in ipairs(section) do
      local c = _G[b:GetName().."Count"]
      local count = (b.info.id) and (b.info.count or 1) or 1
      --containerIndex needs to be updated cuz bag sorting fucks it up
      b.containerIndex = index
      if b.stacks then
         local havestacks
         for i=1, #b.stacks do
            b.stacks[i].containerIndex = index
            if b.stacks[i].info.id == b.info.id then
               havestacks = true
               count = count + (b.stacks[i].info.count or 1)
            end
         end
         if b.stackIndicator then
            if havestacks and famBags.db.profile.layout.buttons.indicators_enabled and b.info.bag ~= KEYRING_CONTAINER then
               b.stackIndicator:Show()
            else
               b.stackIndicator:Hide()
            end
         end
      else
         count = b.info.count or 0
      end
      if count and count > 1 then
         local db = famBags.db.profile.layout.counts
         c:SetText(string.format('|cff%02x%02x%02x%s|r', db.color[1]*255, db.color[2]*255, db.color[3]*255, tostring(count)));
         c:Show()
      else
         c:SetText(nil)
         c:Hide()
      end
   end
end

function famBags.Buttons:ClearAllPoints(bag)
   local b
   for _, i in ipairs(famBags.BagIndexes[bag]) do
      if self[i] then
         for j=1, #self[i] do
            b = self[i][j]
            b:ClearAllPoints()
            b:Hide()
         end
      end
   end
end

function famBags.Buttons:WipeCurrent(bag, full)
   if not self.Current[bag] then
      self.Current[bag] = {}
      return
   end
   for key, b in pairs(self.Current[bag]) do
      if b.info then
         if b.stacks then
            b.stacker = nil
            b.stackerIndex = nil
            b.container = nil
            b.containerIndex = nil
            wipe(b.stacks)
            if famBags.db.profile.layout.buttons.indicators_enabled and b.stackIndicator then
               b.stackIndicator:Hide()
            end
         end
      else
         for _, b1 in pairs(b) do
            if b1.stacks then
               b1.stacker = nil
               b1.stackerIndex = nil
               b1.container = nil
               b1.containerIndex = nil
               wipe(b1.stacks)
               if famBags.db.profile.layout.buttons.indicators_enabled and b1.stackIndicator then
                  b1.stackIndicator:Hide()
               end
            end
         end
         wipe(b)
      end
   end
   if full then
      wipe(self.Current[bag])
   end
end

--local function famBags.Buttons:PreClick(button)
--end

--   UpdateAddOnMemoryUsage()
--   local m1 = GetAddOnMemoryUsage('famBags')
--
--   UpdateAddOnMemoryUsage()
--   print(m1 - GetAddOnMemoryUsage('famBags'))
--   print('-----------------------------')





